package adventure.commands.skills;

import adventure.Adventure;
import adventure.commands.Util;
import adventure.placeable.Fightable;
import adventure.placeable.Mobile;
import colour.Colour;
import colour.ColourUtil;
import player.Player;

/**
 *
 * @author Michael Hanns
 *
 */
public class SingleTargetHeal extends AbstractSkill {

    protected final static String type = "HEALTARGET";
    protected final static Colour typeColour = ColourUtil.white;
    private Colour[] criticalHeal = ColourUtil.criticalHeal;

    public SingleTargetHeal(Adventure a, String[] inputSynonyms, int id) {
        super(a, inputSynonyms, id);

        usage = inputSynonyms[0] + " (<target>)";
        for (int x = 1; x < inputSynonyms.length; x++) {
            usage += ", " + inputSynonyms[x] + " (<target>)";
        }
    }

    public SingleTargetHeal(Adventure a, int id) {
        super(a, new String[]{""}, id);

        usage = "" + " (<target>)";
    }

    @Override
    public String getType() {
        return type;
    }

    @Override
    public Colour getTypeColour() {
        return typeColour;
    }

    @Override
    protected String performSkill(Player player, String[] args) {
        if (args.length == 1) {
            return performSkillOnSelf(player);
        } else if (args.length == 2 && args[1].equalsIgnoreCase("self")) {
            return performSkillOnSelf(player);
        }

        String target = Util.consolidateArguments(args);
        int x = player.x();
        int y = player.y();
        int z = player.z();

        Mobile mob = rooms.getRoomMobile(x, y, z, target);
        if (mob.getID() > 0) {
            return performSkillOnMobile(player, mob);
        } else {
            int targetPlayer = rooms.getRoomPlayer(x, y, z, target);
            if (player.getID() == targetPlayer) {
                return performSkillOnSelf(player);
            } else if (targetPlayer > 0) {
                Player targetSkillPlayer = players.getOnlinePlayer(targetPlayer);

                return performSkillOnPlayer(player, targetSkillPlayer);
            } else {
                return "You don't see any " + target + "!";
            }
        }
    }

    private String performSkillOnMobile(Player healer, Mobile target) {
        if (!target.isDead()) {
            boolean skillSuccess[] = skillSuccess(healer);

            if (!skillSuccess[0]) {
                return ColourUtil.colourise(
                        synonyms[0].toUpperCase().substring(0, 1)
                        + synonyms[0].toLowerCase().substring(1), typeColour) + " failed!";
            }

            double critMod = getCritMod(skillSuccess[1]);

            int dmgHealed = healDamage((Fightable) healer, critMod, (Fightable) target);

            String healoutput = generateHealOutput((Fightable) healer, false, (Fightable) target, true, dmgHealed, 0);
            String healoutputObserver = generateHealOutput((Fightable) healer, false, (Fightable) target, true, dmgHealed, 2);

            if (critMod > 1) {
                healoutput = criticalHeal + healoutput;
                healoutputObserver = criticalHeal + healoutputObserver;
            }

            out.broadcastLocalToPlayer(healer, healoutputObserver);

            consumePrerequisites(healer);

            return healoutput;
        }
        return ColourUtil.colourise(target.getName(), ColourUtil.mobile) + " is already dead!";
    }

    private String performSkillOnSelf(Player healer) {
        boolean skillSuccess[] = skillSuccess(healer);

        if (!skillSuccess[0]) {
            return ColourUtil.colourise(
                    synonyms[0].toUpperCase().substring(0, 1)
                    + synonyms[0].toLowerCase().substring(1), typeColour) + " failed!";
        }

        double critMod = getCritMod(skillSuccess[1]);

        int dmgHealed = healDamage((Fightable) healer, critMod);

        String healoutput = generateHealSelfOutput((Fightable) healer, dmgHealed, false);
        String healoutputObserver = generateHealSelfOutput((Fightable) healer, dmgHealed, true);

        if (critMod > 1) {
            healoutput = criticalHeal + healoutput;
            healoutputObserver = criticalHeal + healoutputObserver;
        }

        out.broadcastLocalToPlayer(healer, healoutputObserver);

        consumePrerequisites(healer);

        return healoutput;
    }

    /*
     * Generates heal output for Player vs Player SingleTargetHeal skill.
     * 
     * @param forWhom - tells the method who to generate the output for. 0 is
     *                  the acting player, 1 is the recieving player,
     *                  2 is all 3rd party observers.
     */
    private String performSkillOnPlayer(Player healer, Player target) {
        if (!target.isDead()) {
            boolean skillSuccess[] = skillSuccess(healer);

            if (!skillSuccess[0]) {
                return ColourUtil.colourise(
                        synonyms[0].toUpperCase().substring(0, 1)
                        + synonyms[0].toLowerCase().substring(1), typeColour) + " failed!";
            }

            double critMod = getCritMod(skillSuccess[1]);

            int dmgHealed = healDamage((Fightable) healer, critMod, (Fightable) target);

            String healoutput = generateHealOutput((Fightable) healer, false, (Fightable) target, true, dmgHealed, 0);
            String healoutputTarget = generateHealOutput((Fightable) healer, false, (Fightable) target, true, dmgHealed, 1);
            String healoutputObserver = generateHealOutput((Fightable) healer, false, (Fightable) target, true, dmgHealed, 2);

            if (critMod > 1) {
                healoutput = criticalHeal + healoutput;
                healoutputTarget = criticalHeal + healoutputTarget;
                healoutputObserver = criticalHeal + healoutputObserver;
            }

            out.broadcastToPlayer(target, healoutputTarget);
            out.broadcastLocalToPlayers(new Player[]{healer, target}, healoutputObserver);

            consumePrerequisites(healer);

            return healoutput;
        }
        return ColourUtil.colourise(target.getAliveName(), ColourUtil.player) + " is already dead!";
    }

    /*
     * Generates heal output for Player vs Player SingleTargetHeal skill.
     * 
     * @param forWhom - tells the method who to generate the output for. 0 is
     *                  the healing player, 1 is the player getting healed,
     *                  2 is all 3rd party observers.
     */
    private String generateHealOutput(Fightable player, boolean mob1,
            Fightable target, boolean mob2, int dmg, int forWhom) {

        Colour colourPlayer;
        Colour colourTarget;

        if (mob1) {
            colourPlayer = ColourUtil.mobile;
        } else {
            colourPlayer = ColourUtil.player;
        }
        if (mob2) {
            colourTarget = ColourUtil.mobile;
        } else {
            colourTarget = ColourUtil.player;
        }

        if (actionFlag == YOU) {
            if (forWhom == 2) {
                return ColourUtil.colourise(player.getAliveName(), colourPlayer) + " "
                        + ColourUtil.colourise(actionDesc, typeColour) + " "
                        + ColourUtil.colourise(target.getAliveName(), colourTarget)
                        + ColourUtil.colourise(" for ", typeColour)
                        + ColourUtil.colourise(dmg + "", ColourUtil.yellow)
                        + ColourUtil.colourise(" damage!", typeColour);
            } else if (forWhom == 1) {
                return ColourUtil.colourise(player.getAliveName(), colourPlayer) + " "
                        + ColourUtil.colourise(actionDesc, typeColour)
                        + ColourUtil.colourise(" you for ", typeColour)
                        + ColourUtil.colourise(dmg + "", ColourUtil.yellow)
                        + ColourUtil.colourise(" damage!", typeColour);
            } else if (forWhom == 0) {
                return ColourUtil.colourise("You " + actionDesc, typeColour) + " "
                        + ColourUtil.colourise(target.getAliveName(), colourTarget)
                        + ColourUtil.colourise(" for ", typeColour)
                        + ColourUtil.colourise(dmg + "", ColourUtil.yellow)
                        + ColourUtil.colourise(" damage!", typeColour);
            }
        } else if (actionFlag == YOUR) {
            if (forWhom == 2) {
                return ColourUtil.colourise(player.getAliveName() + "'s ", colourPlayer)
                        + ColourUtil.colourise(actionDesc, typeColour) + " "
                        + ColourUtil.colourise(target.getAliveName(), colourTarget)
                        + ColourUtil.colourise(" for ", typeColour)
                        + ColourUtil.colourise(dmg + "", ColourUtil.yellow)
                        + ColourUtil.colourise(" damage!", typeColour);
            } else if (forWhom == 1) {
                return ColourUtil.colourise(player.getAliveName() + "'s ", colourPlayer)
                        + ColourUtil.colourise(actionDesc, typeColour)
                        + ColourUtil.colourise(" you for ", typeColour)
                        + ColourUtil.colourise(dmg + "", ColourUtil.yellow)
                        + ColourUtil.colourise(" damage!", typeColour);
            } else if (forWhom == 0) {
                return ColourUtil.colourise("Your " + actionDesc, typeColour) + " "
                        + ColourUtil.colourise(target.getAliveName(), colourTarget)
                        + ColourUtil.colourise(" for ", typeColour)
                        + ColourUtil.colourise(dmg + "", ColourUtil.yellow)
                        + ColourUtil.colourise(" damage!", typeColour);
            }
        } else if (actionFlag == CUSTOM) {
            if (forWhom == 2) {
                return replaceActionKeywords(((Player) player)) + " "
                        + ColourUtil.colourise(target.getAliveName(), colourTarget)
                        + ColourUtil.colourise(" for ", typeColour)
                        + ColourUtil.colourise(dmg + "", ColourUtil.yellow)
                        + ColourUtil.colourise(" damage!", typeColour);
            } else if (forWhom == 1) {
                return replaceActionObserverKeywords(((Player) player)) + " "
                        + ColourUtil.colourise(" you for ", typeColour)
                        + ColourUtil.colourise(dmg + "", ColourUtil.yellow)
                        + ColourUtil.colourise(" damage!", typeColour);
            } else if (forWhom == 0) {
                return replaceActionObserverKeywords(((Player) player)) + " "
                        + ColourUtil.colourise(target.getAliveName(), colourTarget)
                        + ColourUtil.colourise(" for ", typeColour)
                        + ColourUtil.colourise(dmg + "", ColourUtil.yellow)
                        + ColourUtil.colourise(" damage!", typeColour);
            }
        }
        return "";
    }

    private String generateHealSelfOutput(Fightable player, int dmg, boolean observer) {
        if (actionFlag == YOU) {
            if (observer) {
                return ColourUtil.colourise(player.getAliveName(), ColourUtil.player) + " "
                        + ColourUtil.colourise(actionDesc, typeColour)
                        + ColourUtil.colourise(" themself for ", typeColour)
                        + ColourUtil.colourise(dmg + "", ColourUtil.yellow)
                        + ColourUtil.colourise(" damage!", typeColour);
            } else {
                return ColourUtil.colourise("You " + actionDesc + " yourself for ", typeColour)
                        + ColourUtil.colourise(dmg + "", ColourUtil.yellow)
                        + ColourUtil.colourise(" damage!", typeColour);
            }
        } else if (actionFlag == YOUR) {
            if (observer) {
                return ColourUtil.colourise(player.getAliveName() + "'s ", ColourUtil.player)
                        + ColourUtil.colourise(actionDesc, typeColour)
                        + ColourUtil.colourise(" themself for ", typeColour)
                        + ColourUtil.colourise(dmg + "", ColourUtil.yellow)
                        + ColourUtil.colourise(" damage!", typeColour);
            } else {
                return ColourUtil.colourise("Your " + actionDesc + " yourself for ", typeColour)
                        + ColourUtil.colourise(dmg + "", ColourUtil.yellow)
                        + ColourUtil.colourise(" damage!", typeColour);
            }
        } else if (actionFlag == CUSTOM) {
            if (observer) {
                return replaceActionObserverKeywords(((Player) player)) + " "
                        + ColourUtil.colourise(" themself for ", typeColour)
                        + ColourUtil.colourise(dmg + "", ColourUtil.yellow)
                        + ColourUtil.colourise(" damage!", typeColour);
            } else {
                return replaceActionObserverKeywords(((Player) player)) + " "
                        + ColourUtil.colourise(" yourself for ", typeColour)
                        + ColourUtil.colourise(dmg + "", ColourUtil.yellow)
                        + ColourUtil.colourise(" damage!", typeColour);
            }
        }
        return "";
    }

    private String replaceActionKeywords(Player player) {
        String[] words = actionDesc.split(" ");

        for (int x = 0; x < words.length; x++) {
            if (words[x].equals("[YOU]")) {
                words[x] = ColourUtil.colourise("YOU", typeColour);
            } else if (words[x].equals("[YOUR]")) {
                words[x] = ColourUtil.colourise("YOUR", typeColour);
            } else if (words[x].equals("[you]")) {
                words[x] = ColourUtil.colourise("you", typeColour);
            } else if (words[x].equals("[your]")) {
                words[x] = ColourUtil.colourise("your", typeColour);
            } else if (words[x].equals("[You]")) {
                words[x] = ColourUtil.colourise("You", typeColour);
            } else if (words[x].equals("[Your]")) {
                words[x] = ColourUtil.colourise("Your", typeColour);
            } else if (words[x].equals("[PLAYER]")) {
                words[x] = ColourUtil.colourise(player.getAliveName(), ColourUtil.player);
            } else if (words[x].equals("[PLAYERS]")) {
                words[x] = ColourUtil.colourise(player.getAliveName() + "'s", ColourUtil.player);
            } else {
                words[x] = ColourUtil.colourise(words[x], typeColour);
            }
        }

        if (words.length > 0) {
            String out = words[0];

            for (int x = 1; x < words.length; x++) {
                out += " " + words[x];
            }

            return out;
        }

        return actionDesc;
    }

    private String replaceActionObserverKeywords(Player player) {
        String[] words = actionDesc.split(" ");

        for (int x = 0; x < words.length; x++) {
            if (words[x].equals("[YOU]")) {
                words[x] = ColourUtil.colourise(player.getAliveName().toUpperCase(), ColourUtil.player);
            } else if (words[x].equals("[YOUR]")) {
                words[x] = ColourUtil.colourise(player.getAliveName().toUpperCase() + "'s", ColourUtil.player);
            } else if (words[x].equals("[you]")) {
                words[x] = ColourUtil.colourise(player.getAliveName(), ColourUtil.player);
            } else if (words[x].equals("[your]")) {
                words[x] = ColourUtil.colourise(player.getAliveName() + "'s", ColourUtil.player);
            } else if (words[x].equals("[You]")) {
                words[x] = ColourUtil.colourise(player.getAliveName(), ColourUtil.player);
            } else if (words[x].equals("[Your]")) {
                words[x] = ColourUtil.colourise(player.getAliveName() + "'s", ColourUtil.player);
            } else if (words[x].equals("[PLAYER]")) {
                words[x] = ColourUtil.colourise(player.getAliveName(), ColourUtil.player);
            } else if (words[x].equals("[PLAYERS]")) {
                words[x] = ColourUtil.colourise(player.getAliveName() + "'s", ColourUtil.player);
            } else {
                words[x] = ColourUtil.colourise(words[x], typeColour);
            }
        }

        if (words.length > 0) {
            String out = words[0];

            for (int x = 1; x < words.length; x++) {
                out += " " + words[x];
            }

            return out;
        }

        return actionDesc;
    }

    private double getCritMod(boolean critical) {
        if (critical) {
            return 2;
        }

        return 1;
    }

    private int healDamage(Fightable healer, double critMod, Fightable target) {
        int dmgHealed = 0;

        int bound;
        if (isDicePerLevel()) {
            bound = healer.getLevel();
        } else {
            bound = dice;
        }

        // Roll the dice
        for (int x = 0; x < bound; x++) {
            dmgHealed += r.nextInt(diceSides) + 1;
        }

        // Add the attackers influencing stat
        dmgHealed += getMainStatVal(healer);
        dmgHealed *= critMod;

        target.modifyHealth(dmgHealed);

        return dmgHealed;
    }

    private int healDamage(Fightable healer, double critMod) {
        int dmgHealed = 0;

        int bound;
        if (isDicePerLevel()) {
            bound = healer.getLevel();
        } else {
            bound = dice;
        }

        // Roll the dice
        for (int x = 0; x < bound; x++) {
            dmgHealed += r.nextInt(diceSides) + 1;
        }

        // Add the attackers influencing stat
        dmgHealed += getMainStatVal(healer);
        dmgHealed *= critMod;

        healer.modifyHealth(dmgHealed);

        return dmgHealed;
    }

    @Override
    public String getSkillHelp() {
        String output = synonyms[0] + "\n---\n"
                + "Learned at level " + levelLearned
                + "\nUsage(s): " + usage
                + "\nHeals   : ";

        if (isDicePerLevel()) {
            output += "[LVL]d" + diceSides + " + [" + getMainStatName() + "]";
        }

        output += "\n" + description;

        System.out.println("players == null? getSkillHelp" + (players == null));

        return output;
    }
}
